//
// ResourceManager.h
//

#ifndef __RESOURCE_MANAGER_H__
#define __RESOURCE_MANAGER_H__

#include "SevenHeader.h"
#include "DebugFrame.h"
#include "LoaderTask.h"

class CResourceData : public CDebugFrame
{
private:
	class ReferenceObject : public CDebugFrame
	{
	public:
		INTERNAL_MEMORY_CHECK(SN_RESOURCE_DATA_REFERENCE_OBJECT);
		
	public:
		ReferenceObject(void) : hData(NULL), uReferenceCount(1), uSize(0) {} ;
		~ReferenceObject(void) { if(hData) GlobalFree(hData); }

		ReferenceObject(size_t size)
			: hData(NULL), uReferenceCount(1), uSize(size) {
				if(!Reset(size)) throw std::exception(ES_OUT_OF_MEMORY);
		}

		inline UINT IncReference(void) { return ++uReferenceCount; }
		inline UINT DecReference(void) { return --uReferenceCount; }
		inline void *Lock(void) { return (hData ? GlobalLock(hData) : NULL); }
		inline void Unlock(void) { if(hData) GlobalUnlock(hData); }
		inline size_t GetSize(void) const { return uSize; }
		inline bool IsValid(void) const { return hData != 0; }

		inline bool Reset(size_t size) {
			if(hData != NULL) {
				if(NULL == (hData = GlobalReAlloc(hData, GMEM_MOVEABLE, size))) {
					LOG_WRITE(DS_OUT_OF_MEMORY, size);
					return false;
				}
				return true;
			}
			if(NULL == (hData = GlobalAlloc(GMEM_MOVEABLE, size))) {
				LOG_WRITE(DS_OUT_OF_MEMORY, size);
				return false;
			}
			return true;
		}

	private:
		HGLOBAL hData;
		UINT	uReferenceCount;
		size_t	uSize;
	} *_ref;

public:
	INTERNAL_MEMORY_CHECK(SN_RESOURCE_DATA);

public:
	CResourceData(void) {
		_ref = dcnew ReferenceObject;
	}
	CResourceData(size_t size) {
		_ref = dcnew ReferenceObject(size);
	}
	CResourceData(const CResourceData &data) {
		_ref = data._ref;
		_ref->IncReference();
	}
	~CResourceData(void) {
		if(_ref->DecReference() == 0) {
			delete _ref;
		}
	}
	CResourceData &operator=(const CResourceData &data)
	{
		if(&data == this) return *this;
		if(_ref->DecReference() == 0) {
			delete _ref;
		}
		_ref = data._ref;
		_ref->IncReference();
		return *this;
	}
	
	bool operator==(const CResourceData &data) { return _ref == data._ref; }
	bool operator!=(const CResourceData &data) { return _ref != data._ref; }

	void release(void)
	{
		if(_ref->DecReference() == 0) {
			delete _ref;
		}
		_ref = dcnew ReferenceObject;
	}

	inline bool reset(size_t size) { return _ref->Reset(size); }
	inline void *lock(void) { return _ref->Lock(); }
	inline void unlock(void) { return _ref->Unlock(); }
	inline size_t size(void) { return _ref->GetSize(); }
	inline bool valid(void) { return _ref->IsValid(); }

public:
	static const CResourceData null_object;
};

class CResourceCacheTask : public CLoaderTask
{
private:
	CResourceData _pData;
	CCompressedResource *_pRes;
	std::wstring _szLocation;

public:
	INTERNAL_MEMORY_CHECK(SN_RESOURCE_CACHE_TASK);

public:
	CResourceCacheTask(void);
	CResourceCacheTask(CCompressedResource *pResource, const std::wstring &szLocation);
	CResourceCacheTask(const CResourceCacheTask &task);
	~CResourceCacheTask(void);

	void begin(void);
	inline const CResourceData &getData(void) const { return _pData; } ;

	CResourceCacheTask &operator=(const CResourceCacheTask &task);
};

class CResourceManager : public CDebugFrame
{
public:
	INTERNAL_MEMORY_CHECK(SN_RESOURCE_MANAGER);

public:
	CResourceManager(void);
	~CResourceManager(void);

	bool addPackage(const std::wstring &szPackageFileName);
	void removePackage(const std::wstring &szPackageFileName);
	void cleanPackages(void);
	CCompressedResource *getPackage(const std::wstring &szPackageFileName);
	const CResourceData fetchObject(const std::wstring &szObjectUrl);

	bool aliasObject(const std::wstring &szObjectUrl, const std::wstring &szNewName);
	bool cacheObject(const std::wstring &szObjectUrl);
	const CResourceCacheTask *getCacheTask(const std::wstring &szObjectUrl);
	bool isCachedObjectReady(const std::wstring &szObjectUrl);
	bool isEverythingCached(void);
	void cleanCache(void);

	void reset(void);
	void pause(void);
	void resume(void);

private:
	std::map<std::wstring, CCompressedResource *> _resources;
	std::map<std::wstring, CResourceCacheTask *> _cache;

	CLoaderTaskQueue *_queue;
};

#endif // __RESOURCE_MANAGER_H__
